package com.snowfield.lbcommon.utils.commonsexec;

import org.apache.commons.exec.*;
import org.apache.commons.exec.environment.DefaultProcessingEnvironment;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.File;
import java.io.IOException;

/**
 * @author snowfield
 */
public class CommonExecutor {
    private static final Logger LOGGER = LoggerFactory.getLogger(CommonExecutor.class);

    private CommonExecutor() {
    }

    /**
     * 同步方式执行命令
     *
     * @param cmd  命令
     * @param args 参数
     * @return 执行结果
     */
    public static int exec(String cmd, String[] args, String workDirectory) {
        // create commandline
        CommandLine command = new CommandLine(cmd);
        if (args != null) {
            command.addArguments(args);
        }
        LOGGER.info("run command : {}", command.toString());

        // Using LogOutput for the output/error stream
        PumpStreamHandler streamHandler = new PumpStreamHandler(new LogOutput(LogOutput.OUTPU_TLEVEL), new LogOutput(LogOutput.ERROR_LEVEL));

        // default executor
        DefaultExecutor.Builder builder = DefaultExecutor.builder();
        builder.setExecuteStreamHandler(streamHandler);
        if (StringUtils.isNotEmpty(workDirectory) && new File(workDirectory).isDirectory()){
            builder.setWorkingDirectory(new File(workDirectory));
        }
        DefaultExecutor executor = builder.get();

        // This is used to end the process when the JVM exits
        ShutdownHookProcessDestroyer processDestroyer = new ShutdownHookProcessDestroyer();
        executor.setProcessDestroyer(processDestroyer);

        // default run environment
        DefaultProcessingEnvironment env = new DefaultProcessingEnvironment();
        int exitValue = -1;
        try {
            exitValue = executor.execute(command, env.getProcEnvironment());
        } catch (ExecuteException e) {
            LOGGER.error("", e);
        } catch (IOException e) {
            LOGGER.error("", e);
        }
        LOGGER.info("exitValue : {}", exitValue);

        return exitValue;
    }

    /**
     * 异步方式执行命令
     * <p>
     * 在执行此方法时，是另起一个线程来执行的，如果父线程在子线程没有执行结束前就结束了，那么此命令就会执行不完，
     * 所以在使用此方法时，一定注意这个细节
     *
     * @param cmd           命令
     * @param args          参数
     * @param resultHandler 结果处理器
     * @return
     */
    public static void exec(String cmd, String[] args, String workDirectory, DefaultExecuteResultHandler resultHandler) {
        // create commandline
        CommandLine command = new CommandLine(cmd);
        if (args != null) {
            command.addArguments(args);
        }

        LOGGER.info(command.toString());

        // Using LogOutput for the output/error stream
        PumpStreamHandler streamHandler = new PumpStreamHandler(new LogOutput(LogOutput.OUTPU_TLEVEL), new LogOutput(LogOutput.ERROR_LEVEL));

        // default executor
        DefaultExecutor.Builder builder = DefaultExecutor.builder();
        builder.setExecuteStreamHandler(streamHandler);
        if (StringUtils.isNotEmpty(workDirectory) && new File(workDirectory).isDirectory()){
            builder.setWorkingDirectory(new File(workDirectory));
        }
        DefaultExecutor executor = builder.get();

        // This is used to end the process when the JVM exits
        ShutdownHookProcessDestroyer processDestroyer = new ShutdownHookProcessDestroyer();
        executor.setProcessDestroyer(processDestroyer);

        // asynchronous process handling.
        if (resultHandler == null) {
            resultHandler = new DefaultExecuteResultHandler();
        }

        // default run environment
        DefaultProcessingEnvironment env = new DefaultProcessingEnvironment();
        try {
            executor.execute(command, env.getProcEnvironment(), resultHandler);
            new Thread(new ExecuteResultHandler(resultHandler)).start();
        } catch (ExecuteException e) {
            LOGGER.error("", e);
        } catch (IOException e) {
            LOGGER.error("", e);
        } catch (Exception e) {
            LOGGER.error("", e);
        }
    }

    /**
     * 同步方式执行shell命令
     *
     * @param cmd  shell命令,一般是传入一个shell文件名
     * @param args 参数
     * @return 执行结果
     * <p>
     * 适用场景:
     * 返回值为shell文件内部执行的结果,而不是执行shell文件时经常返回的0,1.
     * 比如有一个shell文件为getcores.sh,文件内容为
     * cat /proc/cpuinfo | grep 'processor' | wc -l
     * 此命令是为了得到设备内核数.
     * 单独执行此命令时,如果结果为4,则使用此方法执行后的结果也为4
     * 同样此方法也要求执行的shell文件只能返回一行内容一个结果,而不能返回多行内容.
     * <p>
     * 示例：
     * execShell("sh /usr/local/snowfield/script/test.sh",null);
     */
    public static int execShell(String cmd, String[] args) {
        // create commandline
        CommandLine command = CommandLine.parse(cmd);
        if (args != null) {
            command.addArguments(args);
        }
        LOGGER.info("run command : {}", command.toString());

        // Using LogOutput for the output/error stream
//        ByteArrayOutputStream output = new ByteArrayOutputStream();
//        PumpStreamHandler streamHandler = new PumpStreamHandler(output, new LogOutput(LogOutput.errorLevel));
        PumpStreamHandler streamHandler = new PumpStreamHandler(new LogOutput(LogOutput.OUTPU_TLEVEL), new LogOutput(LogOutput.ERROR_LEVEL));

        // default executor
        DefaultExecutor executor = DefaultExecutor.builder().setExecuteStreamHandler(streamHandler).get();

        // This is used to end the process when the JVM exits
        ShutdownHookProcessDestroyer processDestroyer = new ShutdownHookProcessDestroyer();
        executor.setProcessDestroyer(processDestroyer);

        // default run environment
        DefaultProcessingEnvironment env = new DefaultProcessingEnvironment();

        int exitValue = -1;
        try {
            exitValue = executor.execute(command, env.getProcEnvironment());
        } catch (ExecuteException e) {
            LOGGER.error("", e);
        } catch (IOException e) {
            LOGGER.error("", e);
        }
        LOGGER.info("exitValue : {}", exitValue);

        return exitValue;
    }
}
